#include "graphicsmap.h"
#include "custompixmap.h"

#include <QDir>
#include <QList>
#include <QStringList>
#include <QHBoxLayout>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>

#include <QDebug>

const int MAX_ZOOM_LEVEL = 3;
const int MIN_ZOOM_LEVEL = 1;

class GraphicsMap::Private
{
public:
    Private(GraphicsMap *parent = 0);

    ~Private();

    void clearItemsList();

    void rearrangeItems(int level);

    void rectDetection();

    void checkPos(const QList<int> &itemsToCheck);

    void setZoomingLevel(int level);

    QRectF sceneRect();


    //data members
    QGraphicsView *view;

    QGraphicsScene *scene;

    QGraphicsRectItem *backgroundItem;

    QStringList picsList;

    QList<QGraphicsPixmapItem *> itemsList;

    QString currentPicDir;

    int centerItem;

    int column;

    int zoomLevel;

    int maxHContaintNumber;

    int maxVContaintNumber;

    int itemWidth;

    int itemHeight;

    int step; // move space every steps

private:
    GraphicsMap  *q;

    void levelOneArrangement();

    void levelTwoArrangement();

    void levelThreeArrangement();

    void prepareItemContainers(int amout);

};


GraphicsMap::Private::Private(GraphicsMap *parent):q(parent) , view(NULL) , scene(NULL) , backgroundItem(NULL)
{
    centerItem = -1;
    zoomLevel = MIN_ZOOM_LEVEL;
    maxHContaintNumber = 0;
    maxVContaintNumber = 0;

    view = new QGraphicsView(q);
    view->viewport()->setFixedSize(800 , 480);
    view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    scene = new QGraphicsScene();
    view->setScene(scene);

    step = 10;
}

GraphicsMap::Private::~Private()
{
    if(scene != NULL)
    {
        delete scene;
        scene = NULL;
    }
    if(view != NULL)
    {
        delete view;
        view = NULL;
    }

    // do not delete the items after you deleted scene
    // clearItemsList();
}

QRectF GraphicsMap::Private::sceneRect()
{
    return view->sceneRect();
}

void GraphicsMap::Private::setZoomingLevel(int level)
{
    clearItemsList();
    switch(level)
    {
    case 1:
        levelOneArrangement();
        break;
    case 2:
        levelTwoArrangement();
        break;
    case 3:
        levelThreeArrangement();
        break;
    default:
        break;
    }
}

void GraphicsMap::Private::levelOneArrangement()
{
    column = 2;
    currentPicDir = QString("D:/Project Files/QT\MapWithQGraphics/MapWithQGraphics/pic/");
    QDir dir(currentPicDir);
    int range = dir.entryInfoList().count() - 2;
    prepareItemContainers(range);

}

void GraphicsMap::Private::levelTwoArrangement()
{
    column = 4;
    currentPicDir = QString("D:/Project Files/QT\MapWithQGraphics/MapWithQGraphics/pic1/");
    QDir dir(currentPicDir);
    int range = dir.entryInfoList().count() - 2;
    prepareItemContainers(range);
}

void GraphicsMap::Private::levelThreeArrangement()
{
    column = 8;
    currentPicDir = QString("D:/Project Files/QT\MapWithQGraphics/MapWithQGraphics/pic2/");
    QDir dir(currentPicDir);
    int range = dir.entryInfoList().count() - 2;
    prepareItemContainers(range);
}

void GraphicsMap::Private::prepareItemContainers(int amout)
{
    int row = 0;
    int col = 0;

    backgroundItem = scene->addRect(0 , 0 , itemWidth * (column + 1) , itemHeight * (amout / column));
    backgroundItem->setZValue(-1);

    for(int i = 0 ; i < amout ; ++ i)
    {
        picsList.append(currentPicDir + QString::number(i + 1) + QString(".png"));
        QGraphicsPixmapItem *item = new QGraphicsPixmapItem();

        if(i % column == 0 && i != 0)
        {
            col += 1;
            row = 0;
        }
        scene->addItem(item);
        item->setPos((++ row) * itemWidth , col * itemHeight);
        itemsList.append(item);
    }
    scene->setSceneRect(scene->itemsBoundingRect());

    int midX = scene->width() / 2;
    int midY = scene->height() / 2;
    int xPos = midX - (view->viewport()->width() - itemWidth) / 2;
    int yPos = midY - view->viewport()->height() / 2;

    QRectF rect(QPointF(xPos , yPos) , QSize(view->viewport()->width() , view->viewport()->height()));
    view->setSceneRect(rect);
    rectDetection();
}

void GraphicsMap::Private::clearItemsList()
{
    foreach(QGraphicsPixmapItem *item , itemsList)
    {
        if(item != NULL)
        {
            scene->removeItem(item);
            delete item;
            item = NULL;
        }
    }
    if(backgroundItem != NULL)
        scene->removeItem(backgroundItem);
    itemsList.clear();
    picsList.clear();
}

void GraphicsMap::Private::rectDetection()
{
    QList<int> itemsToCheck;
    QPointF sceneUpLeftPos = view->sceneRect().topLeft();

    int xFactor = (sceneUpLeftPos.x() / itemWidth);
    int yFactor = (sceneUpLeftPos.y() / itemHeight);

    centerItem = yFactor * column + (xFactor - 1);

    for(int i = 0 ; i < maxHContaintNumber ; ++ i)
    {
        for(int j = 0 ; j < maxVContaintNumber ; ++ j)
        {
            itemsToCheck.append(centerItem + j * column + i);
        }
    }
    checkPos(itemsToCheck);
}

void GraphicsMap::Private::checkPos(const QList<int> &itemsToCheck)
{

    int index = -1;
    for(int i = 0 ; i < itemsToCheck.count() ; ++ i)
    {
        index = itemsToCheck.at(i);
        if(index < 0 || index >= itemsList.count())
            continue;

        QGraphicsPixmapItem *item = itemsList.at(index);
        QRectF rect = QRectF(item->pos() , QSizeF(itemWidth , itemHeight));

        if(!view->sceneRect().intersected(rect).isEmpty())
        {
            item->setPixmap(QPixmap(picsList.at(index)));
            item->show();
        }
        else
        {
            item->setPixmap(QPixmap());
            item->hide();
        }
    }
}



GraphicsMap::GraphicsMap(QWidget *parent) :QWidget(parent)
{
    d = new Private(this);
    this->setFixedSize(800 , 480);

    QHBoxLayout *layout = new QHBoxLayout();
    this->setLayout(layout);
    layout->setMargin(0);
    layout->addWidget(d->view);
    this->setContentsMargins(0 , 0 , 0 , 0);

    collectData();
    d->setZoomingLevel(MIN_ZOOM_LEVEL);
}

GraphicsMap::~GraphicsMap()
{
    delete d;
}

void GraphicsMap::keyPressEvent(QKeyEvent *e)
{
    QRectF rect = d->view->sceneRect();
    switch(e->key())
    {
    case Qt::Key_8:
        if(rect.y() - d->step >= 0)
            rect.setY(rect.y() - d->step);
        else
            rect.setY(0);
        break;
    case Qt::Key_2:
        if(rect.y() + d->step <= d->itemHeight * d->column - this->height())
            rect.setY(rect.y() + d->step);
        else
            rect.setY(d->itemHeight * d->column - this->height());
        break;
    case Qt::Key_4:
        if(rect.x() - d->step > d->itemWidth)
            rect.setX(rect.x() - d->step);
        else
            rect.setX(d->itemWidth);
        break;
    case Qt::Key_6:
        if(rect.x() + d->step <= d->itemWidth * (d->column + 1) - this->width())
            rect.setX(rect.x() + d->step);
        else
            rect.setX(d->itemWidth * (d->column + 1) - this->width());
        break;
    case Qt::Key_Plus:
        if(++ d->zoomLevel <= MAX_ZOOM_LEVEL)
            d->setZoomingLevel(d->zoomLevel);
        else
            d->zoomLevel = MAX_ZOOM_LEVEL;
        return;
    case Qt::Key_Minus:
        if(-- d->zoomLevel >= MIN_ZOOM_LEVEL)
            d->setZoomingLevel(d->zoomLevel);
        else
            d->zoomLevel = MIN_ZOOM_LEVEL;
        return;
    default:
        break;
    }

    rect.setSize(QSizeF(d->view->viewport()->width() , d->view->viewport()->height()));
    d->view->setSceneRect(rect);
    d->view->centerOn(rect.width() / 2.0  , rect.height() / 2.0);
    d->rectDetection();
}

void GraphicsMap::collectData()
{
    QSize rect = QPixmap("F:/pic/1.png").size();
    d->maxHContaintNumber = this->width() / rect.width();

    if(d->maxHContaintNumber == 0)
        d->maxHContaintNumber = 2;
    else
    {
        if(this->width() %  rect.width() >= 2)
            d->maxHContaintNumber += 2;
        else if(this->width() % rect.width() == 1)
            d->maxHContaintNumber += 1;
    }

    d->maxVContaintNumber = this->height() / rect.height();
    if(d->maxVContaintNumber == 0)
        d->maxVContaintNumber = 2;
    else
    {
        if(this->height() % rect.height() >= 2)
            d->maxVContaintNumber += 2;
        else if(this->height() % rect.height() == 1)
            d->maxVContaintNumber += 1;
    }

    d->itemWidth = rect.width();
    d->itemHeight = rect.height();
}


